package scfg.utils;

import java.util.*;
import java.io.*;

import scfg.output.Display;
import scfg.output.RNAFormattedFile;
import desposito6.utils.CommandLine;

public class MFECategorizer {

	public static PriorityQueue<MFEPair> pq;
	public static int total, count, BUFFER = 6;
	public static BufferedOutputStream pout;
	public static Scanner pin;
	private static Display output;
	private static boolean rename;
	private static boolean skip;
	private static boolean bpdist;
	private static String compareDesc1;
	private static String compareDesc2;

	static {
		output = new Display("MFECategorizer");
		compareDesc1 = "nat";
		compareDesc2 = "mfe";
	}

	static {
		pq = new PriorityQueue<MFEPair>();
		total = count = 0;
	}

	public static boolean processFile(File f) {
		if (!f.exists())
			return false;
		if (skip && f.getName().matches("\\d\\d\\d\\d_.+\\.rna")) {
			String[] arr = f.getName().split("_");
			int dist = Integer.parseInt(arr[0]);
			MFEPair mp = new MFEPair(f.getAbsolutePath(), dist);
			pq.add(mp);
			return true;
		}
		try {
			RNAFormattedFile rnaFile = new RNAFormattedFile(f);
			String nat = rnaFile.get(compareDesc1);
			String mfe = rnaFile.get(compareDesc2);
			if (nat == null || mfe == null) {
				// natural structure or mfe predicted structure is null.
				return false;
			}
			int dist = -1;
			if (bpdist) {
				dist = compareStructuresRnaDist(nat, mfe);
			} else
				dist = compareStructures(nat, mfe);
			if (dist >= 0) {
				MFEPair mp = new MFEPair(f.getAbsolutePath(), dist);
				pq.add(mp);
				if (rename)
					f.renameTo(new File(f.getAbsolutePath().replaceAll(
							"\\d\\d\\d\\d+_+", "").replaceAll(
							f.getName(),
							Display.buf(dist + "", BUFFER, '0') + "_"
									+ f.getName())));
				return true;
			}
		} catch (Exception e) {
			System.out.println("There was an error: " + e.getMessage());
		}
		return false;
	}

	public static int compareStructures(String nat, String str) {
		double f = Compare.getFMeasure(nat, str);
		int dist = (int) ((f) * 100000);
		return dist;
	}

	public static int compareStructuresRnaDist(String nat, String str) {
		// TODO
		try {
			pout.write(nat.getBytes());
			pout.write("\n".getBytes());
			pout.write(str.getBytes());
			pout.write("\n".getBytes());
			pout.flush();
			String line;
			if (pin.hasNextLine())
				line = pin.nextLine();
			else
				return -1;
			return Integer.parseInt(line.replaceAll("P:\\s+", "").trim());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return -1;
	}

	public static void processDir(File base) {
		if (!base.exists())
			return;
		output.out("Opening " + base.getAbsolutePath());
		File[] files = base.listFiles();
		total += files.length;
		int currCount = 0;
		long start = System.currentTimeMillis();
		Stack<File> dirs = new Stack<File>();
		for (File f : files) {
			if (f.isDirectory() && f.getName().charAt(0) != '.') {
				dirs.push(f);
			} else if (f.getName().endsWith(".rna")) {
				if (!processFile(f))
					output.out(f.getAbsolutePath() + " failed");
			} else
				output.out(f.getAbsolutePath() + " Not processed");
			count++;
			CommandLine.DisplayBar(files.length, currCount++, System
					.currentTimeMillis()
					- start);
		}
		CommandLine.DisplayBarFinish();
		while (!dirs.isEmpty())
			processDir(dirs.pop());
	}

	public static List<String> printList() {
		List<String> ranked = new ArrayList<String>();
		while (!pq.isEmpty())
			output.out(pq.poll().toString());
		System.out.println("\n\n\t[File Written Successfully]");
		return ranked;
	}

	public static String helpMessage() {
		return "\n[scfg.utils.StructureAggregator]\n\t"
				+ "* -h Display help message.\n\t"
				+ "* -s Source folder.\n\t"
				+ "* -d Destination folder - Copy. (Optional)\n\t"
				+ "* -delete Delete ambiguous files. (Optional) - Not implemented.\n\t"
				+ "* -o Output filename. (Optional)\n\t"
				+ "* -v Display verbose output. (Optional)\n\t"
				+ "* -desc1 First structure for comparison - Default = 'nat'. (Optional)\n\t"
				+ "* -desc2 Second structure for comparison - Default = 'mfe'. (Optional)\n\t"
				+ "* -move How many ranked files to select. Positive for best, negative for worst. (Optional)\n\t"
				+ "* -bpdist Quantify structure accuracy by Base Pair Distance. Default F-Measure. (Optional)\n\t"
				+ "* -rename Prepend filenames with score. (Optional)\n\n";
	}

	/**
	 * <pre>
	 * -h Display help message.
	 * -s Source folder.
	 * -d Destination folder - Copy. (Optional)
	 * -delete Delete ambiguous files. (Optional) - Not implemented.
	 * -o Output filename. (Optional)
	 * -v Display verbose output. (Optional)
	 * -desc1 First structure for comparison - Default = 'nat'. (Optional)
	 * -desc2 Second structure for comparison - Default = 'mfe'. (Optional)
	 * -move How many ranked files to select. Positive for best, negative for worst. (Optional)\n\t
	 * -bpdist Quantify structure accuracy by Base Pair Distance. (Optional)
	 * -rename Prepend filenames with score. (Optional)
	 * </pre>
	 * 
	 * -s /home/david/Documents/REU/RNA/rna_files/ -d
	 * /home/david/Documents/REU/RNA/ambiguous_files/ -v -delete
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		if (CommandLine.containsArg(args, "-h") >= 0) {
			output.out(helpMessage());
			output.finalizeDisplay();
			return;
		}
		int temp = CommandLine.containsArg(args, "-o");
		if (temp >= 0 && temp < args.length - 1) {
			output = new Display(args[temp + 1], args[temp + 1] + "_error.log",
					true);
		}
		temp = CommandLine.containsArg(args, "-desc1");
		if (temp >= 0 && temp < args.length - 1) {
			compareDesc1 = args[temp + 1];
		}
		temp = CommandLine.containsArg(args, "-desc2");
		if (temp >= 0 && temp < args.length - 1) {
			compareDesc2 = args[temp + 1];
		}
		temp = CommandLine.containsArg(args, "-s");
		String sourceName = null;
		if (temp >= 0 && temp < args.length - 1) {
			sourceName = args[temp + 1];
		}
//		temp = CommandLine.containsArg(args, "-d");
//		String destinationName = null;
//		if (temp >= 0 && temp < args.length - 1) {
//			destinationName = args[temp + 1];
//		} else {
//			destinationName = ".ambiguous_rna/";
//		}
		output.setVerbose(CommandLine.containsArg(args, "-v") >= 0);
		skip = CommandLine.containsArg(args, "-skip") >= 0;
		bpdist = CommandLine.containsArg(args, "-bpdist") >= 0;
		boolean delete = CommandLine.containsArg(args, "-delete") >= 0;
		rename = CommandLine.containsArg(args, "-rename") >= 0;
		File srcFile = new File(sourceName);

		if (bpdist) {
			try {
				Process p = Runtime.getRuntime().exec(
						new String[] { "/bin/sh", "-c", "RNAdistance -DP" });
				pout = new BufferedOutputStream(p.getOutputStream());
				pin = new Scanner(p.getInputStream());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		processDir(srcFile);
		List<String> ambiguousFiles = printList();
		// if (destinationName != null)
		// sortedMover(ambiguousFiles, destinationName);
		// if (CommandLine.containsArg(args, "-delete") >= 0)
		// sortedDeleter(ambiguousFiles);
		output.out("\n\n\n\t[EXITING StructureAggregator.main]");
		output.finalizeDisplay();
		if (bpdist) {
			try {
				pin.close();
				pout.write("@\n".getBytes());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	private static class MFEPair implements Comparable<MFEPair> {
		public int p;
		public String filename;

		public MFEPair(String fname, int p) {
			this.filename = fname;
			this.p = p;
		}

		@Override
		public int compareTo(MFEPair that) {
			return this.p - that.p;
		}

		@Override
		public String toString() {
			return this.p + "\t" + this.filename;
		}
	}
}
